/*
 * file_sys.cpp
 *
 *  Created on: Jul 15, 2009
 *      Author: azoulaii
 */

#include "file_sys.h"

FileSystem::FileSystem(LowLevelDiskServices *diskService){
	_diskService = diskService;
}

int FileSystem::createFile(type flag){
	if(flag != Normal && flag != SoftLink)
		return -1;
	int i_number = _diskService->allocateInode();

	_diskService->setInodeType(i_number,flag);

	return i_number;
}
int FileSystem::createDir(){
	int i_number = _diskService->allocateInode();
	_diskService->setInodeType(i_number,Directory);
	return i_number;
}

type FileSystem::getFileType(int i_number){
	return _diskService->getInodeType(i_number);
}

void FileSystem::f_write(int i_number, char *buffer, int offset, int nBytes){

	if(this->_diskService->getInodeType(i_number) != Normal)
		return;
	general_data *g = this->_diskService->getGeneralData();
	int blkSize= g->blk_size ;

	if ((offset+nBytes)>this->_diskService->getFileSize(i_number)) //In case I need to allocate blocks
	{    // Check with Itamar this calc
		this->_diskService->setFileSize(i_number,offset+nBytes);
	}

	char *block = new char[g->blk_size];
	int blockIndex = offset/g->blk_size;
	int blockNumber = this->_diskService->getDataBlock(i_number,blockIndex);

	this->_diskService->readBlock(blockNumber,block);

	int offsetInBlock = offset%g->blk_size;

	for(int i = 0; i < nBytes; ++i){

		block[offsetInBlock] = buffer[i];
		offsetInBlock = (offsetInBlock + 1)%blkSize;
		if(offsetInBlock == 0){
			this->_diskService->writeBlock(blockNumber,block);
			++blockIndex;
			blockNumber = this->_diskService->getDataBlock(i_number,blockIndex);
			this->_diskService->readBlock(blockNumber,block);
		}
	}

	this->_diskService->writeBlock(blockNumber,block);

}

int FileSystem::f_read(int i_number,char *buffer, int offset,int nBytes){

	if(this->_diskService->getInodeType(i_number) != Normal)
		return NOT_A_NORMAL_FILE;

	general_data *g = this->_diskService->getGeneralData();

	char *block = new char[g->blk_size];
	int blockIndex = offset/g->blk_size;
	int blockNumber = this->_diskService->getDataBlock(i_number,blockIndex);

	this->_diskService->readBlock(blockNumber,block);

	int offsetInBlock = offset%g->blk_size;
	int i;

	int size = this->_diskService->getFileSize(i_number);
	for(i = 0; i < nBytes && i < size ; ++i){
		buffer[i] = block[offsetInBlock];
		offsetInBlock = (offsetInBlock + 1)% g->blk_size;
		if(offsetInBlock == 0){
			++blockIndex;
			blockNumber = this->_diskService->getDataBlock(i_number,blockIndex);
			this->_diskService->readBlock(blockNumber,block);
		}
	}

	return i;
}

int FileSystem::d_write(int i_number ,list *dir_l){

	if(this->_diskService->getInodeType(i_number) != Directory)
		return NOT_A_DIRECTORY;

	general_data *g = this->_diskService->getGeneralData();

	int size = dir_l->number*sizeof(directory_entry);

	this->_diskService->setFileSize(i_number,size);

	int blockIndex = 0;
	int blockNumber = this->_diskService->getDataBlock(i_number,blockIndex);
	char *dirs = (char*)dir_l->enteries;
	char *block = new char[g->blk_size];

	for(int i = 0; i < size; ++i){
		block[i%g->blk_size] = dirs[i];
		if(i%g->blk_size == g->blk_size - 1){
			this->_diskService->writeBlock(blockNumber,block);
			++blockIndex;
			blockNumber = this->_diskService->getDataBlock(i_number,blockIndex);
			for(int j = 0;j < g->blk_size; ++j)
				block[j] = 0;
		}
	}

	this->_diskService->writeBlock(blockNumber,block);

	return 0;
}

list *FileSystem::d_read(int i_number){
	if(this->_diskService->getInodeType(i_number) != Directory)
		return NULL;

	general_data *g = this->_diskService->getGeneralData();

	int size = this->_diskService->getFileSize(i_number);

	int num_of_directories = size/sizeof(directory_entry);

	list *dir_l = new list;
	dir_l->number = num_of_directories;

	dir_l->enteries = new directory_entry[dir_l->number];

	int blockIndex = 0;
	int blockNumber = this->_diskService->getDataBlock(i_number,blockIndex);

	char *block = new char[g->blk_size];
	this->_diskService->readBlock(blockNumber,block);
	char *dirs = (char*)dir_l->enteries;
	for(int i = 0; i < size; ++i){
		dirs[i] = block[i%g->blk_size];
		if(i%g->blk_size == g->blk_size - 1){
			++blockIndex;
			blockNumber = this->_diskService->getDataBlock(i_number,blockIndex);
			this->_diskService->readBlock(blockNumber,block);
		}
	}

	return dir_l;
}

int FileSystem::f_delete(int i_number){
	if(this->_diskService->getInodeType(i_number) != Directory)
		this->_diskService->freeInode(i_number);
	return 1;
}

int FileSystem::d_delete(int i_number){
	general_data *g = this->_diskService->getGeneralData();
	if(i_number == g->root_iNode)
		return TRY_TO_DELETE_ROOT;
	if(this->_diskService->getInodeType(i_number) == Directory)
		if(this->_diskService->getFileSize(i_number) == 0)
			this->_diskService->freeInode(i_number);
	return 1;
}
